from app.Dao.GoalDao.GoalDao import GoalDao
from app.Dao.GoalDao.GoalCateDao import GoalCateDao
from datetime import date
from app.Models.Goal.GoalCateModel import GoalCate  # 分类模型（校验用）
from app.Models.AdminModels.UserModels.UserModel import User  # 用户模型（校验用）
from app.db import db


class GoalService:
    """目标业务逻辑层，封装业务规则和用户提示（适配双外键）"""

    @staticmethod
    def add_goal(goal_data):
        """
        新增目标（含业务校验）
        :param goal_data: 目标数据字典，需包含：first_cate_id、goal_name、start_date、end_date、goal_status；可选：user_id
        :return: (bool, f"提示信息")
        """
        try:
            # 1. 必填字段校验（user_id 可选，允许未分配用户）
            required_fields = ["first_cate_id", "goal_name", "start_date", "end_date"]
            for field in required_fields:
                if field not in goal_data or not goal_data[field]:
                    return False, f"新增失败：{field} 为必填字段"

            # 2. 外键合法性校验
            # 2.1 一级分类存在性校验
            cate_exists = GoalCate.query.get(goal_data["first_cate_id"])
            if not cate_exists:
                return False, f"新增失败：一级分类 ID {goal_data['first_cate_id']} 不存在"
            # 2.2 用户存在性校验（如果传了 user_id）
            if "user_id" in goal_data and goal_data["user_id"]:
                user_exists = User.query.get(goal_data["user_id"])
                if not user_exists:
                    return False, f"新增失败：用户 ID {goal_data['user_id']} 不存在"

            # 3. 日期格式和逻辑校验
            try:
                # 转换日期（支持字符串/date对象）
                start_date = goal_data["start_date"]
                start_date = date.fromisoformat(start_date) if isinstance(start_date, str) else start_date
                end_date = goal_data["end_date"]
                end_date = date.fromisoformat(end_date) if isinstance(end_date, str) else end_date
            except ValueError:
                return False, f"新增失败：日期格式错误（请使用 YYYY-MM-DD 格式）"

            # 3.1 开始日期 <= 结束日期
            if start_date > end_date:
                return False, f"新增失败：开始日期 {start_date.strftime('%Y-%m-%d')} 不能晚于结束日期 {end_date.strftime('%Y-%m-%d')}"

            # 4. 目标状态合法性校验
            if goal_data["goal_status"] not in [0, 1, 2]:
                return False, f"新增失败：目标状态只能是 0（未完成）、1（完成）、2（暂缓）"

            # 5. 调用 DAO 层新增
            new_goal = GoalDao.add_goal(goal_data)
            # 拼接提示信息（区分是否分配用户）
            user_desc = f"，所属用户 ID：{new_goal.user_id}" if new_goal.user_id else "（未分配用户）"
            return True, f"新增目标成功！目标 ID：{new_goal.id}，目标名称：{new_goal.goal_name}{user_desc}"

        except Exception as e:
            db.session.rollback()
            return False, f"新增目标失败：{str(e)}"

    @staticmethod
    def delete_goal(goal_id):
        """
        根据 ID 删除目标（含业务校验）
        :param goal_id: 目标 ID
        :return: (bool, f"提示信息")
        """
        try:
            # 1. 目标 ID 合法性校验
            if not isinstance(goal_id, int) or goal_id <= 0:
                return False, f"删除失败：目标 ID {goal_id} 不合法（必须是正整数）"

            # 2. 调用 DAO 层删除
            delete_result = GoalDao.delete_goal(goal_id)
            return True, f"删除目标成功！目标 ID：{goal_id}" if delete_result else False

        except Exception as e:
            db.session.rollback()
            return False, f"删除目标失败：{str(e)}"

    @staticmethod
    def update_goal(goal_id, update_data):
        """
        根据 ID 更新目标（含业务校验）
        :param goal_id: 目标 ID
        :param update_data: 待更新数据字典（可选字段：user_id、first_cate_id、goal_name、start_date、end_date、goal_status）
        :return: (bool, f"提示信息")
        """
        try:
            # 1. 目标 ID 合法性校验
            if not isinstance(goal_id, int) or goal_id <= 0:
                return False, f"更新失败：目标 ID {goal_id} 不合法（必须是正整数）"

            # 2. 目标存在性校验
            existing_goal = GoalDao.get_goal_by_id(goal_id)
            if not existing_goal:
                return False, f"更新失败：目标 ID {goal_id} 不存在"

            # # 3. 可更新字段校验（仅允许更新指定字段）
            # valid_fields = ["user_id", "first_cate_id", "goal_name", "start_date", "end_date", "goal_status"]
            # for key in update_data.keys():
            #     if key not in valid_fields:
            #         return False, f"更新失败：字段 {key} 不允许更新（仅支持 {valid_fields}）"

            # 4. 外键合法性校验（如果更新外键）
            # 4.1 分类 ID 校验
            if "first_cate_id" in update_data and update_data["first_cate_id"]:
                cate_exists = GoalCate.query.get(update_data["first_cate_id"])
                if not cate_exists:
                    return False, f"更新失败：一级分类 ID {update_data['first_cate_id']} 不存在"
            # 4.2 用户 ID 校验（传了才校验，允许传 None 取消分配）
            if "user_id" in update_data and update_data["user_id"] is not None:
                user_exists = User.query.get(update_data["user_id"])
                if not user_exists:
                    return False, f"更新失败：用户 ID {update_data['user_id']} 不存在"

            # 5. 日期逻辑校验（如果更新日期）
            if "start_date" in update_data or "end_date" in update_data:
                # 获取原日期（避免只传一个日期导致逻辑错误）
                original_start = existing_goal["start_date"]
                original_end = existing_goal["end_date"]
                start_date = update_data.get("start_date", original_start)
                end_date = update_data.get("end_date", original_end)

                # 转换日期格式
                try:
                    start_date = date.fromisoformat(start_date) if isinstance(start_date, str) else start_date
                    end_date = date.fromisoformat(end_date) if isinstance(end_date, str) else end_date
                except ValueError:
                    return False, f"更新失败：日期格式错误（请使用 YYYY-MM-DD 格式）"

                # 日期逻辑校验
                if start_date > end_date:
                    return False, f"更新失败：开始日期 {start_date.strftime('%Y-%m-%d')} 不能晚于结束日期 {end_date.strftime('%Y-%m-%d')}"

            # 6. 目标状态合法性校验（如果更新状态）
            if "goal_status" in update_data and int(update_data["goal_status"]) not in [0, 1, 2]:
                return False, f"更新失败：目标状态只能是 0（未完成）、1（完成）、2（暂缓）"

            # 7. 调用 DAO 层更新
            updated_goal = GoalDao.update_goal(goal_id, update_data)
            return True, f"更新目标成功！目标 ID：{updated_goal.id}，更新字段：{list(update_data.keys())}"

        except Exception as e:
            db.session.rollback()
            return False, f"更新目标失败：{str(e)}"

    @staticmethod
    def get_goal_by_id(goal_id):
        """
        根据 ID 查询目标（返回格式化结果）
        :param goal_id: 目标 ID
        :return: (bool, f"提示信息", 目标字典) -> 成功时第3个参数为目标数据，失败时为 None
        """
        try:
            # 1. 目标 ID 合法性校验
            if not isinstance(goal_id, int) or goal_id <= 0:
                return False, f"查询失败：目标 ID {goal_id} 不合法（必须是正整数）", None

            # 2. 调用 DAO 层查询
            goal = GoalDao.get_goal_by_id(goal_id)
            if not goal:
                return False, f"查询失败：目标 ID {goal_id} 不存在", None

            return True, f"查询目标成功！目标 ID：{goal_id}", goal

        except Exception as e:
            return False, f"查询目标失败：{str(e)}", None

    @staticmethod
    def query_goals(filters=None, page=1, per_page=30):
        """
        分页查询目标列表（返回格式化结果）
        :param filters: 过滤条件字典
        :param page: 页码
        :param per_page: 每页条数
        :return: (bool, f"提示信息", 分页结果字典) -> 成功时第3个参数为分页数据，失败时为 None
        """
        try:
            # 1. 分页参数合法性校验
            if not isinstance(page, int) or page < 1:
                return False, f"查询失败：页码 {page} 不合法（必须是正整数）"
            if not isinstance(per_page, int) or per_page < 1 or per_page > 100:
                return False, f"查询失败：每页条数 {per_page} 不合法（必须是 1-100 之间的整数）"

            # 2. 调用 DAO 层查询
            page_result = GoalDao.query_goals(filters=filters, page=page, per_page=per_page)
            total = page_result["total"]

            # 按结果条数返回不同提示
            if total == 0:
                return True, page_result
            return True, page_result

        except Exception as e:
            return False, f"查询目标列表失败：{str(e)}"

    # 获取所有的目标一级分类
    @staticmethod
    def get_all_first_cate(user_id: int = None):
        cate_list = GoalCateDao.query_goal_cates(user_id)['items']
        return True, cate_list
